package com.yanqu.road.server.gameplayer.module.activity.worldcup;

import com.yanqu.road.entity.activity.ActivityInfo;
import com.yanqu.road.entity.activity.worldcup.*;
import com.yanqu.road.entity.config.goods.GoodsInfo;
import com.yanqu.road.entity.config.system.NormalWeightItem;
import com.yanqu.road.entity.enums.eGamePlayerEventType;
import com.yanqu.road.entity.enums.eLogMoneyType;
import com.yanqu.road.entity.enums.eSystemId;
import com.yanqu.road.entity.log.LogWorldCupKick;
import com.yanqu.road.entity.log.LogWorldCupRecruit;
import com.yanqu.road.entity.log.LogWorldCupSkill;
import com.yanqu.road.logic.bussiness.player.WorldCupActivityBussiness;
import com.yanqu.road.logic.helper.RandomWeightConfigHelper;
import com.yanqu.road.logic.helper.WeightHelper;
import com.yanqu.road.pb.activity.WorldCupProto;
import com.yanqu.road.server.gameplayer.GamePlayer;
import com.yanqu.road.server.gameplayer.module.base.GeneralModule;
import com.yanqu.road.server.gameplayer.module.player.CurrencyModule;
import com.yanqu.road.server.manger.activity.ActivityMgr;
import com.yanqu.road.server.manger.activity.worldcup.WorldCupMgr;
import com.yanqu.road.server.manger.config.SystemOpenMgr;
import com.yanqu.road.server.manger.log.LogMgr;
import com.yanqu.road.server.manger.system.CommonMgr;
import com.yanqu.road.server.pb.WorldCupActivityPb;
import com.yanqu.road.server.protocol.GameErrorCode;
import com.yanqu.road.server.protocol.Protocol;
import com.yanqu.road.utils.RandomHelper;
import com.yanqu.road.utils.config.Config;
import com.yanqu.road.utils.date.DateHelper;
import com.yanqu.road.utils.property.Property;
import com.yanqu.road.utils.property.PropertyHelper;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;

public class WorldCupModule extends GeneralModule {
    public WorldCupModule(GamePlayer player) {
        super(player);
    }

    private UserWorldCupData userData;
    //解数最高等级
    private int maxSkillLevel;
    //解数总累计等级
    private int totalSkillLevel;
    private LinkedList<UserWorldCupBallerData> ballerList = new LinkedList<>();
    private List<UserWorldCupBallerData> entryBallers = new ArrayList<>();
    private List<UserWorldCupBallerData> recycleBallers = new ArrayList<>();
    private static RandomHelper random = new RandomHelper();

    @Override
    public boolean loadData() {
        ActivityInfo activityInfo = WorldCupMgr.getActivityInfo();
        if (ActivityMgr.activityInShowTime(activityInfo)) {
            userData = WorldCupActivityBussiness.getUserWorldCupData(player.getUserId(), activityInfo.getActivityId());

            List<UserWorldCupBallerData> list = WorldCupActivityBussiness.getUserWorldCupBallerData(player.getUserId(), activityInfo.getActivityId());
            for (UserWorldCupBallerData ballerData : list) {
                ballerList.addLast(ballerData);
            }
        }
        return true;
    }

    @Override
    public boolean afterLoadData() {
        return true;
    }

    @Override
    public boolean saveData() {
        if (userData != null) {
            if (userData.isInsertOption()) {
                WorldCupActivityBussiness.addUserWorldCupData(userData);
            } else if (userData.isUpdateOption()) {
                WorldCupActivityBussiness.updateUserWorldCupData(userData);
            }
        }

        for (UserWorldCupBallerData data : getAndRemoveBallerList()) {
            if (data.isInsertOption()) {
                WorldCupActivityBussiness.addUserWorldCupBallerData(data);
            } else if (data.isUpdateOption()) {
                WorldCupActivityBussiness.updateUserWorldCupBallerData(data);
            }
        }

        for (UserWorldCupBallerData data : this.recycleBallers) {
            if (data.isInsertOption()) {
                WorldCupActivityBussiness.addUserWorldCupBallerData(data);
            } else if (data.isUpdateOption()) {
                WorldCupActivityBussiness.updateUserWorldCupBallerData(data);
            }
        }
        this.recycleBallers.clear();

        return true;
    }

    @Override
    public void afterLogin() {

        if (!SystemOpenMgr.systemOpen(player, eSystemId.WorldCupActivity.getValue())) {
            return;
        }

        syncConfig();

        syncUserData();
        //总等级成就
        player.notifyListener(eGamePlayerEventType.WorldCupSkillLevel.getValue(), BigInteger.valueOf(totalSkillLevel));

    }

    public void initSystem() {
        if (!SystemOpenMgr.systemOpen(player, eSystemId.WorldCupActivity.getValue())) {
            return;
        }
        syncConfig();
        syncUserData();
    }

    public void syncConfig() {

        WorldCupConfig config = WorldCupMgr.getConfig();
        if (config == null || !ActivityMgr.activityInShowTime(config.getActivityInfo())) {
            return;
        }
        String language = player.getLanguage();
        WorldCupProto.WorldCupConfigSyncMsg.Builder configSync = WorldCupActivityPb.builderSyncConfigMsg(config, language);

        player.sendPacket(Protocol.U_WORLDCUP_SYNC_CONFIG, configSync);
    }

    /**
     * 同步玩家数据
     */
    public void syncUserData() {

        WorldCupConfig config = WorldCupMgr.getConfig();
        if (config == null || !ActivityMgr.activityInShowTime(config.getActivityInfo())) {
            return;
        }

        if (SystemOpenMgr.systemOpen(player, eSystemId.WorldCupActivity.getValue())) {
            if (userData == null) {
                initUserData();
            }
        }

        if (userData == null) {
            return;
        }
        totalSkillLevel = 0;
        for (WorldCupSkill skill : userData.getSkillMap().values()) {
            if (skill.getLevel() > maxSkillLevel) {
                maxSkillLevel = skill.getLevel();
            }
            totalSkillLevel += skill.getLevel() - 1;
        }

        WorldCupProto.WorldCupDataSyncMsg.Builder syncMsg = WorldCupProto.WorldCupDataSyncMsg.newBuilder();
        WorldCupProto.UserWorldCupData.Builder builder = WorldCupActivityPb.buildUserData(userData, getBallerList());
        syncMsg.setUserData(builder);
        player.sendPacket(Protocol.U_WORLDCUP_SYNC_DATA, syncMsg);
    }

    /**
     * 初始化玩家数据
     */
    public synchronized void initUserData() {
        // 系统未解锁
        if (!SystemOpenMgr.systemOpen(player, eSystemId.WorldCupActivity.getValue())) {
            return;
        }

        ActivityInfo activityInfo = WorldCupMgr.getActivityInfo();

        // 没有活动
        if (!ActivityMgr.activityInShowTime(activityInfo) || WorldCupMgr.getConfig() == null) {
            return;
        }

        // 已经有数据了
        if (userData != null && userData.getActivityId() == activityInfo.getActivityId()) {
            return;
        }

        UserWorldCupData tmpUserData = new UserWorldCupData();
        tmpUserData.setActivityId(activityInfo.getActivityId());
        tmpUserData.setUserId(getUserId());
        tmpUserData.setSkillMap(new HashMap<>());
        tmpUserData.setScore(0);
        tmpUserData.setRecruitAmount(0);
        tmpUserData.setInsertOption();

        userData = tmpUserData;
    }

    public void sendUserDataMsg() {
        WorldCupProto.WorldCupGetUserDataRespMsg.Builder msg = WorldCupProto.WorldCupGetUserDataRespMsg.newBuilder();
        WorldCupProto.UserWorldCupData.Builder builder = WorldCupActivityPb.buildUserData(userData, ballerList);
        msg.setRet(0);
        msg.setUserData(builder);
        player.sendPacket(Protocol.U_WORLDCUP_GET_DATA, msg);
    }

    public List<UserWorldCupBallerData> getBallerList() {
        List<UserWorldCupBallerData> tempList = new ArrayList<>();
        synchronized (ballerList) {
            tempList = new ArrayList<>(ballerList);
        }
        return tempList;
    }

    public List<UserWorldCupBallerData> getAndRemoveBallerList() {
        List<UserWorldCupBallerData> tempList = new ArrayList<>();
        synchronized (ballerList) {
            tempList = new ArrayList<>(ballerList);
            tempList.addAll(entryBallers);
            //已参赛球员数据保存后释放掉
            entryBallers.clear();
        }
        return tempList;
    }

    public void setRecycleBallers(List<UserWorldCupBallerData> ballers) {
        synchronized (ballerList) {
            ballerList.clear();
        }
        this.recycleBallers = ballers;
    }

    /**
     * 汴梁世界杯招募
     */
    public int recruitBaller(int num) {
        WorldCupConfig config = WorldCupMgr.getConfig();
        if (config == null) {
            return GameErrorCode.E_ERROR_DATA;
        }

        List<WorldCupSkillConfig> randomSkills = new ArrayList<>();
        Map<Integer, Integer> weightInfos = new HashMap<>();

        //道具检测
        int useItemNum = config.getOneRecruitCostNum() * num;
        if (num == 10) {
            //十连限制,招募了N名球员后才能解锁
            if (userData.getRecruitAmount() < config.getTenRecruitNeedValue()) {
                return GameErrorCode.E_WORLDCUP_SCORE_TEN_RECRUIT_LIMIT;
            }
            useItemNum = config.getTenRecruitCostNum();
        }
        CurrencyModule currencyModule = player.getModule(CurrencyModule.class);
        Property costProperty = new Property(config.getBallUniformItemId(), BigInteger.valueOf(useItemNum));
        if (!currencyModule.currencyIsEnough(costProperty)) {
            return GameErrorCode.E_BAG_ITEM_NO_ENOUGH;
        }

        int recruitAmount = userData.getRecruitAmount();
        //复制一份解数数据，全部招募后再结算
        Map<Integer, WorldCupSkill> copyUserSkill = new HashMap<>();
        for (WorldCupSkill skill : userData.getSkillMap().values()) {
            copyUserSkill.put(skill.getType(), skill.clone());
        }

        //招募结果
        List<UserWorldCupBallerData> recruitBallers = new ArrayList<>();
        //奖励
        Property reward = new Property();
        //解数经验变化
        Map<Integer, Integer> expMap = new HashMap<>();

        if (Config.isDebug()) {
            WorldCupMgr.getLogger().info("玩家{},最高解数等级为{}", player.getUserId(),
                    maxSkillLevel);
        }
        //招募计算
        for (int i = 0; i < num; i++) {

            randomSkills.clear();
            //计算解数权重
            for (Map.Entry<Integer, List<WorldCupSkillConfig>> levelConfigs : config.getSkillConfig().entrySet()) {
                int type = levelConfigs.getKey();
                WorldCupSkill skill = null;
                if (copyUserSkill.containsKey(type)) {
                    skill = copyUserSkill.get(type);
                    if (skill.getLevel() > levelConfigs.getValue().size()) {
                        //等级数据异常
                        skill.setLevel(levelConfigs.getValue().size());
                    }

                    if (skill.getLevel() <= 0) {
                        //等级数据异常
                        skill.setLevel(1);
                    }
                } else {
                    skill = new WorldCupSkill();
                    skill.setType(type);
                    skill.setLevel(1);
                    skill.setExp(0);
                    skill.setCalcWeightAmount(0);
                    copyUserSkill.put(type, skill);
                }

                skill.setCalcWeightAmount(skill.getCalcWeightAmount() + 1);

                int takeLevel = skill.getLevel();
                int idx = randomSkills.size();
                randomSkills.add(levelConfigs.getValue().get(takeLevel - 1));
                int weight = 0;
                /*
                 * if 解数最高等级-某解数等级≥a，某解数权重=max（d+（随机次数*b），c) else 解数权重=d
                 * */
                if (maxSkillLevel - takeLevel >= config.getSkillWeightParam_1()) {
                    int calcWeightAmount = skill.getCalcWeightAmount();
                    weight = BigDecimal.valueOf(config.getSkillWeightParam_4()).
                            add(BigDecimal.valueOf(calcWeightAmount).multiply(BigDecimal.valueOf(config.getSkillWeightParam_2()))).intValue();
                    weight = weight > config.getSkillWeightParam_3() ? weight : config.getSkillWeightParam_3();
                } else {
                    skill.setCalcWeightAmount(0);
                    weight = config.getSkillWeightParam_4();
                }

                weightInfos.put(idx, weight);
                if (Config.isDebug()) {
                    WorldCupMgr.getLogger().info("玩家{},解数权重计算(type={},level={})={}", player.getUserId(), type, takeLevel, weight);
                }
            }

            int randIdx = WeightHelper.getRandomValue(weightInfos);
            if (-1 == randIdx || randIdx >= randomSkills.size()) {
                return GameErrorCode.E_ERROR_DATA;
            }

            WorldCupSkillConfig skillConfig = randomSkills.get(randIdx);
            WorldCupRecruitInfo recruitResult = (WorldCupRecruitInfo) WeightHelper.getRandomResult(skillConfig.getWeightRecruits());
            if (null == recruitResult) {
                return GameErrorCode.E_ERROR_DATA;
            }
            if (Config.isDebug()) {
                WorldCupMgr.getLogger().info("玩家{},随机到解数(type={},level={}),球员品质{}", player.getUserId(),
                        recruitResult.getType(), recruitResult.getLevel(), recruitResult.getQuality());
            }


            //获得解数经验
            WorldCupPlayerConfig ballerConfig = config.getBallerConfig().get(recruitResult.getQuality());
            if (null == ballerConfig) {
                return GameErrorCode.E_ERROR_DATA;
            }
            int expAdd = ballerConfig.getExpAdd();
            WorldCupSkill resultSkill = copyUserSkill.get(recruitResult.getType());
            updateSkillLevel(config, resultSkill, expAdd);

            int totalExpAdd = expAdd;
            if (expMap.containsKey(recruitResult.getType())) {
                totalExpAdd = expMap.get(recruitResult.getType()) + expAdd;
            }
            expMap.put(recruitResult.getType(), totalExpAdd);

            //随机技巧值
            int ability = random.next(recruitResult.getMinValue(), recruitResult.getMaxValue());

            UserWorldCupBallerData newBaller = new UserWorldCupBallerData();
            newBaller.setActivityId(config.getActivityInfo().getActivityId());
            newBaller.setUserId(player.getUserId());
            newBaller.setBallerId(++recruitAmount);
            newBaller.setType(recruitResult.getType());
            newBaller.setLevel(recruitResult.getLevel());
            newBaller.setRecruitTime(DateHelper.getCurrentTime());
            newBaller.setQuality(recruitResult.getQuality());
            newBaller.setEntryTime(0);
            newBaller.setAbility(ability);
            newBaller.setInsertOption();

            if (Config.isDebug()) {
                WorldCupMgr.getLogger().info("玩家{},获得球员{},品质={},技巧值={}", player.getUserId(),
                        newBaller.getBallerId(), newBaller.getQuality(), newBaller.getAbility());
            }

            recruitBallers.add(newBaller);

            GoodsInfo goodsInfo = CommonMgr.getGoodsInfo(Integer.valueOf(ballerConfig.getRewards()));
            if (goodsInfo == null) {
                return GameErrorCode.E_BAG_ITEM_NO_ENOUGH;
            }

            Property randomReward = new Property();
            String extendParam = goodsInfo.getExtendParam();
            Property property = RandomWeightConfigHelper.getRandomProperty(extendParam);
            randomReward.addProperty(property);
            reward.addProperty(randomReward);


            LogMgr.addLogWorldCupRecruit(new LogWorldCupRecruit(
                    userData.getUserId(),userData.getActivityId(),num==10?2:1,
                    recruitResult.getQuality(),ability,PropertyHelper.parsePropertyToString(randomReward),
                    recruitResult.getType(),recruitResult.getLevel()
            ));
        }

        userData.setRecruitAmount(recruitAmount);


        //解数变化结算
        int newTotalSkillLevel = 0;
        int newMaxSkillLevel = 0;
        for (WorldCupSkill skill : copyUserSkill.values()) {
            if (skill.getLevel() > newMaxSkillLevel) {
                newMaxSkillLevel = skill.getLevel();
            }
            //减去初始1级
            newTotalSkillLevel += skill.getLevel() - 1;

            userData.getSkillMap().put(skill.getType(), skill);
        }
        if (newMaxSkillLevel != this.maxSkillLevel) {
            this.maxSkillLevel = newMaxSkillLevel;
        }
        if (newTotalSkillLevel != this.totalSkillLevel){
            this.totalSkillLevel = newTotalSkillLevel;

            //总等级成就
            player.notifyListener(eGamePlayerEventType.WorldCupSkillLevel.getValue(), BigInteger.valueOf(totalSkillLevel));
        }

        userData.setUpdateOption();

        //球员获得
        for (UserWorldCupBallerData baller : recruitBallers) {
            synchronized (ballerList) {
                ballerList.addLast(baller);
            }
        }

        //奖励获得
        if (!reward.isNothing()) {
            currencyModule.addCurrency(reward, eLogMoneyType.WorldCup, eLogMoneyType.WorldCupRecruit);
        }
        //道具扣除
        currencyModule.removeCurrency(costProperty, eLogMoneyType.WorldCup, eLogMoneyType.WorldCupRecruit);


        WorldCupProto.WorldCupRecruitRespMsg.Builder respMsg = WorldCupActivityPb.buildRecruitMsg(copyUserSkill, expMap, recruitBallers, PropertyHelper.parsePropertyToString(reward));
        player.sendPacket(Protocol.U_WORLDCUP_RECRUIT, respMsg);

        sendUserDataMsg();

        return 0;
    }

    //更新解数等级
    private boolean updateSkillLevel(WorldCupConfig config, WorldCupSkill skill, int expAdd) {
        if (skill.getLevel() >= config.getSkillConfig().get(skill.getType()).size()) {
            //满级了
            return false;
        }
        if (Config.isDebug()) {
            WorldCupMgr.getLogger().info("玩家{},解数(type={},level={},exp={})获得经验{}", player.getUserId(),
                    skill.getType(), skill.getLevel(), skill.getExp(), expAdd);
        }
        int oLevel = skill.getLevel();
        skill.setExp(skill.getExp() + expAdd);
        boolean isUpdate = false;
        for (WorldCupSkillConfig skillConfig : config.getSkillConfig().get(skill.getType())) {
            if (skill.getLevel() > skillConfig.getLevel()) {
                continue;
            }
            if (skillConfig.getCost() == 0) {
                break;
            }
            if (skill.getExp() >= skillConfig.getCost()) {
                skill.setLevel(skill.getLevel() + 1);
                skill.setExp(skill.getExp() - skillConfig.getCost());
                isUpdate = true;
            } else {
                break;
            }
        }
        int nLevel = skill.getLevel();
        if (Config.isDebug()) {
            WorldCupMgr.getLogger().info("玩家{},获得经验后解数变为(type={},level={},exp={})", player.getUserId(),
                    skill.getType(), skill.getLevel(), skill.getExp());
        }

        if (skill.getLevel() > maxSkillLevel) {
            maxSkillLevel = skill.getLevel();
            if (Config.isDebug()) {
                WorldCupMgr.getLogger().info("玩家{},最高解数等级变为{}", player.getUserId(),
                        maxSkillLevel);
            }
        }
        LogMgr.addLogWorldCupSkill(new LogWorldCupSkill(
                userData.getUserId(),userData.getActivityId(),skill.getType(),oLevel,nLevel,expAdd
        ));
        return isUpdate;
    }

    /**
     * 汴梁世界杯参赛
     */
    public int ballerEntry(boolean isAllEntry) {
        if (ballerList == null || ballerList.isEmpty()) {
            return GameErrorCode.E_ERROR_DATA;
        }

        WorldCupConfig config = WorldCupMgr.getConfig();
        if (config == null) {
            return GameErrorCode.E_ERROR_DATA;
        }

        int entryCount = isAllEntry ? ballerList.size() : 1;

        List<UserWorldCupBallerData> resultEntryBallers = new ArrayList<>();
        List<WorldCupProto.WorldCupEntryTemp> entryBallerMsg = new ArrayList<>();
        int scoreAdd = 0;

        for (int i = 0; i < entryCount; i++) {
            UserWorldCupBallerData ballerData = ballerList.get(i);

            NormalWeightItem scoreMulti = WeightHelper.getRandomResult(config.getScoreRandomConfig());
            if (null == scoreMulti) {
                return GameErrorCode.E_ERROR_DATA;
            }
            //实际获得积分=技巧值*（1+奖励加成百分比）向上取整
            int ability = ballerData.getAbility();
            int ballerScore = BigDecimal.valueOf(ability).multiply(BigDecimal.valueOf(1000).add(BigDecimal.valueOf(scoreMulti.getValue().intValue())))
                    .divide(BigDecimal.valueOf(1000), 0, BigDecimal.ROUND_UP).intValue();

            scoreAdd += ballerScore;

            if (Config.isDebug()) {
                WorldCupMgr.getLogger().info("玩家{},球员(ballerId={},quailty={},ability={}),千分比加成={}参赛获得积分{}", player.getUserId(),
                        ballerData.getBallerId(), ballerData.getQuality(), ballerData.getAbility(), scoreMulti.getValue().intValue(), ballerScore);
            }

            resultEntryBallers.add(ballerData);

            WorldCupProto.WorldCupEntryTemp.Builder ballerMsg = WorldCupProto.WorldCupEntryTemp.newBuilder();
            ballerMsg.setBallerId(ballerData.getBallerId());
            ballerMsg.setScoreAdd(ballerScore);
            ballerMsg.setScoreMulti(scoreMulti.getValue().intValue());
            ballerMsg.setQuality(ballerData.getQuality());
            ballerMsg.setAbility(ballerData.getAbility());
            entryBallerMsg.add(ballerMsg.build());


            LogMgr.addLogWorldCupKick(new LogWorldCupKick(
                    userData.getUserId(),userData.getActivityId(),ballerData.getQuality(),
                    ballerData.getAbility(),ballerScore,scoreMulti.getValue().intValue()
            ));
        }

        userData.setScore(userData.getScore() + scoreAdd);
        userData.setUpdateOption();
        //获得积分道具
        player.getModule(CurrencyModule.class).addCurrency(config.getScoreItemId() + "=" + scoreAdd, eLogMoneyType.WorldCup, eLogMoneyType.WorldCupEntry);

        synchronized (ballerList) {
            if (isAllEntry) {
                ballerList.clear();
            } else {
                ballerList.removeFirst();
            }

            for (UserWorldCupBallerData ballerData : resultEntryBallers) {
                ballerData.setEntryTime(DateHelper.getCurrentTime());
                ballerData.setUpdateOption();
                entryBallers.add(ballerData);
            }
        }

        //排行榜
        player.notifyListener(eGamePlayerEventType.WorldCupScoreRank.getValue(), BigInteger.valueOf(userData.getScore()));

        WorldCupProto.WorldCupEntryRespMsg.Builder respMsg = WorldCupProto.WorldCupEntryRespMsg.newBuilder();
        respMsg.setRet(0);
        respMsg.addAllEntryBallers(entryBallerMsg);

        player.sendPacket(Protocol.U_WORLDCUP_ENTRY, respMsg);

        sendUserDataMsg();

        return 0;
    }
}
